//
// Created by hunan on 25-3-25.
//

#include "socket_util.h"
#include "socket_errno.h"
#include "socket_address.h"
#include "./../write_read_module/buffer.h"
#include <arpa/inet.h>
#include <cerrno>
#include <cstdint>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int SocketUtil::Read(int socket, WriteReadBuffer & buffer)
{
    if (socket < 0)
    {
        return EmSocketErrno_BAD_DESCRIPT;
    }
    if (buffer.BufferFreeSize() < 65535)
    {
        buffer.ExpendBuffer(65535);
    }
    int result = read(socket, buffer.BufferEnd(), buffer.BufferFreeSize());
    if (result == 0 || (result < 0 && errno != EAGAIN))
    {
        return EmSocketErrno_READ_FAILED;
    }
    else
    {
        if (result > 0)
        {
            buffer.MovePosition(result);
        }
        return EmSocketErrno_SUCCESS;
    }
}

int SocketUtil::Write(int socket, WriteReadBuffer & buffer)
{
    int result = write(socket, buffer.BufferData(), buffer.BufferDataSize());
    if (result < 0)
    {
        return EmSocketErrno_SEND_FAILED;
    }
    else
    {
        buffer.RemoveSize(result);
        return EmSocketErrno_SUCCESS;
    }
}

int SocketUtil::Accept(int socket, SocketAddress & address)
{
    sockaddr_in sai = {};
    socklen_t length = sizeof(sai);
    int result = accept(socket, reinterpret_cast<struct sockaddr *>(&sai), &length);
    if (result < 0)
    {
        return -1;
    }
    address = SocketAddress::Address(sai);
    return result;
}

int SocketUtil::BindAddress(int socket, const SocketAddress & address)
{
    return BindAddress(socket, IPPROTO_IP, AF_INET, address.PortToNumber(), address.AddressToString());
}

int SocketUtil::BindAddress(int socket, int type, int family, uint16_t port, const std::string & ip)
{
    if (type == IPPROTO_IPV6)
    {
        return EmSocketErrno_ACCEPT_FAILED;
    }
    else
    {
        sockaddr_in sai = {};
        sai.sin_family = family;
        if (port > 0)
        {
            sai.sin_port = htons(port);
        }
        if (!ip.empty())
        {
            inet_pton(family, ip.c_str(), &sai.sin_addr.s_addr);
        }
        else
        {
            sai.sin_addr.s_addr = htonl(INADDR_ANY);
        }
        int result = bind(socket, reinterpret_cast<struct sockaddr *>(&sai), sizeof(sai));
        return result > 0 ? EmSocketErrno_SUCCESS : EmSocketErrno_ACCEPT_FAILED;
    }
}
